package com.glsc.ngateway.platform.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.glsc.ngateway.common.api.common.dto.PageDataResult;
import com.glsc.ngateway.common.api.common.enums.DictConstant;
import com.glsc.ngateway.common.api.common.enums.PlatformConstant;
import com.glsc.ngateway.common.api.common.utils.PageUtil;
import com.glsc.ngateway.common.api.flowable.dto.FlowDoTaskRespDto;
import com.glsc.ngateway.common.api.flowable.dto.FlowableCopyDto;
import com.glsc.ngateway.common.api.flowable.dto.ForwardFlowableParamDto;
import com.glsc.ngateway.common.api.flowable.dto.TaskDefinitionDto;
import com.glsc.ngateway.common.api.flowable.dto.param.*;
import com.glsc.ngateway.common.api.flowable.dto.task.config.FlowConfigDto;
import com.glsc.ngateway.common.api.flowable.dto.task.config.FlowConfigSearchDto;
import com.glsc.ngateway.common.api.flowable.feign.IFeignFlowableService;
import com.glsc.ngateway.common.api.flowable.resp.*;
import com.glsc.ngateway.common.api.ldap.dto.LdapEhrEmpDto;
import com.glsc.ngateway.common.api.ldap.feign.IFeignLdapService;
import com.glsc.ngateway.common.api.ldap.resp.AppEhrResponse;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.ProcessEditConfig;
import com.glsc.ngateway.common.base.domain.mysql.gateway.platform.flow.FlowFormEvent;
import com.glsc.ngateway.common.base.enums.itflow.FlowConfigConfigTypeEnum;
import com.glsc.ngateway.common.base.enums.itflow.FlowConfigValueTypeEnum;
import com.glsc.ngateway.common.base.exception.PlatformException;
import com.glsc.ngateway.common.base.repo.mysql.gateway.platform.FlowFormEventRepo;
import com.glsc.ngateway.platform.aspect.UserCache;
import com.glsc.ngateway.platform.service.itflow.config.ItFlowConfigService;
import com.glsc.ngateway.platform.service.itflow.config.ProcessEditConfigService;
import com.glsc.ngateway.platform.utils.JsonTool;
import com.glsc.ngateway.platform.utils.RequestTool;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Nullable;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhouzp
 * @date 2022/12/8
 * flowable流程引擎工具类
 */
@Service
public class FlowCommonService {

    Logger logger = LoggerFactory.getLogger(FlowCommonService.class);


    @Resource
    @Qualifier("transactionManagerMysql")
    private PlatformTransactionManager transactionManager;

    @Resource
    private UserCache userCache;

    @Resource
    private IFeignFlowableService feignFlowableService;

    @Resource
    private IFeignLdapService iFeignLdapService;

    @Resource
    private FlowFormEventRepo flowFormEventRepo;

    @Resource
    private RequestTool requestTool;

    @Resource
    private ItFlowConfigService itFlowConfigService;

    @Resource
    private ProcessEditConfigService processEditConfigService;

    @Resource
    private UserService userService;

    /**
     * 创建流程
     */
    public FlowableResponse<ResponseCreateDto> create(ParamCreateDto paramCreateDto) {
        logger.info("FlowableFeignService创建流程:入参 ：" + paramCreateDto);

        //1.流程创建要素检查
        if (paramCreateDto == null || StrUtil.isEmpty(paramCreateDto.getUserid())) {
            throw PlatformException.error("创建流程参数为空,paramCreateDto:" + paramCreateDto);
        }

        Map<String, String> fixUserGroup = this.initTaskUserGroupByConfig(paramCreateDto.getProcessDefinitionKey(), paramCreateDto.getTaskAssignee());
        paramCreateDto.setTaskAssignee(fixUserGroup);
        Map<String, String> fixVariableGroup = this.initTaskConditionalVarByConfig(paramCreateDto.getProcessDefinitionKey(), paramCreateDto.getConditionalVariable());
        paramCreateDto.setConditionalVariable(fixVariableGroup);

        //模版方式统一处理流程增加流程节点变量的默认处理，这要求所有的节点都需要在process_edit_config增加配置项，所有节点默认都是xxHas，可配置跳过
        List<ProcessEditConfig> processEditConfigList = processEditConfigService.findAllByProcessType(paramCreateDto.getProcessDefinitionKey());
        Map<String, String> taskVarMap = new HashMap<>();
        processEditConfigList.forEach(item -> {
            taskVarMap.put(item.getNodeType() + "Has", "1");
        });
        taskVarMap.forEach((k,v)->{
            fixVariableGroup.putIfAbsent(k,v);//如果没有设置，则系统默认增加节点存在
        });

        //2.发起创建流程
        FlowableResponse<ResponseCreateDto> createFlowResponse = feignFlowableService.create(paramCreateDto);
        logger.info("FlowableFeignService创建流程:返回：" + createFlowResponse);

        return createFlowResponse;
    }


    /**
     * 始化节点流程分配人,默认为空
     */
    public Map<String, String> initTaskUserGroupListByConfig(@NotNull String flowKey, @Nullable Map<String, List<String>> overGroupMap) {
        Map<String, String> stringOverGroupMap = null;
        if (MapUtil.isNotEmpty(overGroupMap)) {
            stringOverGroupMap = new HashMap<>();
            for (String key : overGroupMap.keySet()) {
                stringOverGroupMap.put(key, String.join(",", overGroupMap.get(key)));
            }

        }
        Map<String, String> stringResultGroupMap = this.initTaskUserGroupByConfig(flowKey, stringOverGroupMap);
        return stringResultGroupMap;
    }

    /**
     * 始化节点流程分配人,默认为空
     */
    public Map<String, String> initTaskUserGroupByConfig(@NotNull String flowKey, @Nullable Map<String, String> overGroupMap) {
        //先根据配置查询角色
        List<FlowConfigDto> configList = itFlowConfigService.query(FlowConfigSearchDto.builder().processDefKey(flowKey).configType(FlowConfigConfigTypeEnum.TASK_ASSIGNEE.getCode()).build());
        //聚合<taskKey, List<FlowConfig>>>
        Map<String, List<FlowConfigDto>> configMap = configList.stream().collect(Collectors.groupingBy(FlowConfigDto::getTaskDefKey));
        //转成<节点变量Param名称(ITFlowTaskParamEnum), List<userAccount>>
        Map<String, List<String>> configTaskUserGroup = new HashMap<>();
        configMap.forEach((proKey, configs) -> configs.forEach(config -> {
            List<String> accounts = userService.findAccountsByType(FlowConfigValueTypeEnum.seek(config.getValueType()), config.getValue());
            if (CollectionUtils.isNotEmpty(accounts)) {
                List<String> accountList = accounts.stream().distinct().collect(Collectors.toList());
                //支持多个group配置，为了兼容旧版本定义的groupName
                Arrays.stream(config.getTaskAssigneeGroupName().split(",")).filter(Strings::isNotBlank).forEach(g ->
                        configTaskUserGroup.put(g, accountList)
                );
            }
        }));
        //再根据代码默认逻辑查询角色
        //合并两者，代码优先,将配置的人员列表覆盖
        if (MapUtils.isNotEmpty(overGroupMap)) {
            Map<String, List<String>> overGroupListMap = new HashMap<>();
            for (String key : overGroupMap.keySet()) {
                if (StrUtil.isEmpty(overGroupMap.get(key))) {
                    overGroupListMap.put(key, new ArrayList<>());
                } else {
                    overGroupListMap.put(key, Arrays.stream(overGroupMap.get(key).split(",")).collect(Collectors.toList()));
                }
            }
            configTaskUserGroup.putAll(overGroupListMap);
        }
        if (CollectionUtil.isEmpty(configTaskUserGroup)) {
            throw PlatformException.error("流程审批人初始化失败，当前无审批人");
        }
        return configTaskUserGroup.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> StringUtils.join(entry.getValue(), ",")));
    }

    /**
     * 初始化其他变量
     */
    public Map<String, String> initTaskConditionalVarByConfig(@NotNull String flowKey, @Nullable Map<String, String> overGroupMap) {
        //先根据配置查询变量
        List<FlowConfigDto> configList = itFlowConfigService.query(FlowConfigSearchDto.builder().processDefKey(flowKey).configType(FlowConfigConfigTypeEnum.CONDITIONAL_VARIABLE.getCode()).build());
        //<变量名称, 变量值> TODO:将变量进行分类，按分类进行处理
        Map<String, String> configConditionalVariable = configList.stream().collect(Collectors.toMap(FlowConfigDto::getConditionalVariableKeyName
                , c -> CollectionUtils.isNotEmpty(c.getValue()) ? String.join(",", c.getValue()) : Strings.EMPTY));
        //合并两者，配置优先,将代码的变量列表覆盖
        if (MapUtils.isNotEmpty(overGroupMap)) {
            configConditionalVariable.putAll(overGroupMap);
        }

        return configConditionalVariable;
    }

    /**
     * 查询网关任务信息
     *
     * @param taskId 任务ID
     * @return
     */
    public FlowableResponse<ResponseSituationDto> getTaskInfo(String taskId) {
        return this.getTaskInfo(taskId,PlatformConstant.SYS_ID_GATEWAY);
    }

    /**
     * 查询任务信息
     *
     * @param taskId 任务ID
     * @return
     */
    public FlowableResponse<ResponseSituationDto> getTaskInfo(String taskId, String sysid) {
        if (StrUtil.isEmpty(taskId)) {
            throw PlatformException.error("任务Id不能为空");
        }
        if (StrUtil.isEmpty(sysid)) {
            sysid = PlatformConstant.SYS_ID_GATEWAY;
        }
        return feignFlowableService.getTaskInfo(taskId, sysid);
    }

    /**
     * 保存表单数据或审核节点前，检查流程节点的合法性信息，主要检查任务存在、流程关联基本信息存在
     * 同时，返回检查的这些必要要素
     *
     * @param taskId flowable流程任务ID
     * @return 返回任务信息、流程基本信息、可能关联的产品基本信息
     */
    public FlowDoTaskRespDto validCheckTaskBeforeSaveFormOrAuditTask(String taskId) {
        //返回数据
        FlowDoTaskRespDto respDto = new FlowDoTaskRespDto();

        //流程基础信息校验
        if (StrUtil.isEmpty(taskId)) {
            throw PlatformException.errorWithOutMail("流程任务ID不能为空");
        } else {
            taskId = StrUtil.trim(taskId);
            respDto.setTaskId(taskId);
        }

        FlowableResponse<ResponseSituationDto> flowableTaskInfoResponse = this.getTaskInfo(taskId, PlatformConstant.SYS_ID_GATEWAY);
        if (flowableTaskInfoResponse.getStatus() != 0) {
            throw PlatformException.errorWithOutMail("流程引擎未查询到任务信息" + taskId);
        } else {
            respDto.setCurrentTaskInfo(flowableTaskInfoResponse.getData());
            respDto.setProcessId(flowableTaskInfoResponse.getData().getProcessId());//后续保存策略会用到processId
            respDto.setTaskDefinitionKey(flowableTaskInfoResponse.getData().getCurrentTaskDefinitionKey());
            respDto.setProcessDefineKey(flowableTaskInfoResponse.getData().getProcessDefineKey());
        }
        //以上为基础校验

        return respDto;
    }


    /**
     * 初始化备注，因为备注可能为空，但是flowable端不能为null，所以这里统一做初始化处理
     *
     * @param commentDto 评论意见对象
     */
    public DoTaskCommentDto initCommentDto(DoTaskCommentDto commentDto) {
        if (commentDto == null) {
            commentDto = new DoTaskCommentDto();
        }
        if (StrUtil.isEmpty(commentDto.getFullMessage())) {
            commentDto.setFullMessage("");
        } else {
            commentDto.setFullMessage(commentDto.getFullMessage());
        }

        return commentDto;
    }


    /**
     * 流程审核通过
     */
    public FlowableResponse<ResponseMsgDto> taskAuditPass(ParamDoTaskDto paramDotaskDto) {
        if (StrUtil.isEmpty(paramDotaskDto.getSysid())) {
            paramDotaskDto.setSysid(PlatformConstant.SYS_ID_GATEWAY);
        }

        logger.info("FlowableFeignService批准流程:入参paramDotaskDto：{}", paramDotaskDto);
        FlowableResponse<ResponseMsgDto> flowableResponse = feignFlowableService.doTask(paramDotaskDto);
        logger.info("FlowableFeignService批准流程:返回：" + flowableResponse);
        return flowableResponse;
    }


    /**
     * 查询流程状态
     *
     * @return
     */
    public ResponseSituationDto getProcessInfo(String processId) {
        logger.info("入参processId：" + processId);
        FlowableResponse<ResponseSituationDto> ret = feignFlowableService.getProcessInfo(processId);
        logger.info("getProcessInfo:返回ret：" + ret);
        ResponseSituationDto returnData = null;
        if (ret != null && ret.getStatus() == 0) {
            returnData = ret.getData();
        }
        return returnData;
    }


    /**
     * 流程审核驳回，返回至上一个节点
     */
    public FlowableResponse<FlowDoTaskRespDto> taskAuditReject(ParamRollbackDto paramRollbackDto) {
        if (StrUtil.isEmpty(paramRollbackDto.getSysid())) {
            paramRollbackDto.setSysid(PlatformConstant.SYS_ID_GATEWAY);
        }

        logger.info("FlowableFeignService驳回流程:入参paramRollbackDto：{}", paramRollbackDto);
        FlowableResponse result = feignFlowableService.rollback(paramRollbackDto);
        logger.info("FlowableFeignService驳回流程，返回ret：" + result);

        FlowDoTaskRespDto respDto = new FlowDoTaskRespDto();
        if (validFlowableResponse(result)) {
            respDto.setTaskId(paramRollbackDto.getTaskId());
            result.setData(respDto);
        }

        return result;
    }


    /**
     * 检查流程返回数据是否正常
     */
    private boolean validFlowableResponse(FlowableResponse ret) {
        if (null == ret) {
            logger.info("Flowable接口返回为空");
            throw PlatformException.error("Flowable接口返回为空");
        }

        logger.info("flowable response:{}", ret.toString());
        if (ret.getStatus() != 0) {//返回0是正常
            String errMsg = "Flowable接口返回异常，错误状态码=" + ret.getStatus() + "，原因：" + ret.getMsg();
            logger.error(errMsg);
            throw PlatformException.error(errMsg);
        } else {
            logger.info("Flowable接口返回正常，返回信息=" + ret.toString());
            return true;
        }
    }


    /**
     * 流程我的待办分页查询
     *
     * @param dealUser 【必填】 我的待办，需要提供dealUser
     */
    public Page<ResponseSituationDto> findProcessToDoPage(String dealUser, ParamSearchDto paramSearchDto, Integer pageNo, Integer pageSize) {
        pageNo = PageUtil.initPageNo(pageNo);
        pageSize = PageUtil.initPageSize(pageSize);
        if (StrUtil.isEmpty(dealUser)) {
            throw PlatformException.error("流程查询我的待办，处理人信息不能为空");
        }


        if (paramSearchDto == null) {
            paramSearchDto = new ParamSearchDto();
        }
        paramSearchDto.setDealUser(dealUser);
        return getProcessSituationDtoPage(pageNo, pageSize, paramSearchDto);
    }

    /**
     * 流程我的已办分页查询
     */
    @Deprecated
    public Page<ResponseSituationDto> findProcessDonePage(String title, String processDefineKey, String account, Integer pageNo, Integer pageSize) {

        ParamSearchDto paramSearchDto = new ParamSearchDto();
        paramSearchDto.setSysid(PlatformConstant.SYS_ID_GATEWAY);
        paramSearchDto.setTitle(StrUtil.trim(title));
        paramSearchDto.setProcessDefineKey(StrUtil.trim(processDefineKey));
        paramSearchDto.setInvolveUser(account);
        return getProcessSituationDtoPage(pageNo, pageSize, paramSearchDto);
    }

    /**
     * 流程我的已办分页查询
     *
     * @param involveUser 【必填】我的已办，需要提供involveUser
     */
    public Page<ResponseSituationDto> findProcessDonePage(String involveUser, ParamSearchDto paramSearchDto, Integer pageNo, Integer pageSize) {
        pageNo = PageUtil.initPageNo(pageNo);
        pageSize = PageUtil.initPageSize(pageSize);
        if (StrUtil.isEmpty(involveUser)) {
            throw PlatformException.error("流程查询我的已办，处理人信息不能为空");
        }


        if (paramSearchDto == null) {
            paramSearchDto = new ParamSearchDto();
        }
        paramSearchDto.setInvolveUser(involveUser);
        return getProcessSituationDtoPage(pageNo, pageSize, paramSearchDto);
    }

    /**
     * 流程我的发起分页查询
     */
    @Deprecated
    public Page<ResponseSituationDto> findProcessStartPage(String title, String processDefineKey, String account, Integer pageNo, Integer pageSize) {
        Page<ResponseSituationDto> page = null;
        ParamSearchDto paramSearchDto = new ParamSearchDto();
        paramSearchDto.setSysid(PlatformConstant.SYS_ID_GATEWAY);
        paramSearchDto.setTitle(StrUtil.trim(title));
        paramSearchDto.setProcessDefineKey(StrUtil.trim(processDefineKey));
        paramSearchDto.setStartUser(account);
        return getProcessSituationDtoPage(pageNo, pageSize, paramSearchDto);
    }


    /**
     * 流程我的发起分页查询
     *
     * @param startUser 【必填】我的发起的，需要提供发起人 startUser
     */
    public Page<ResponseSituationDto> findProcessStartPage(String startUser, ParamSearchDto paramSearchDto, Integer pageNo, Integer pageSize) {
        pageNo = PageUtil.initPageNo(pageNo);
        pageSize = PageUtil.initPageSize(pageSize);
        if (StrUtil.isEmpty(startUser)) {
            throw PlatformException.error("流程查询我的发起的，发起人信息不能为空");
        }


        if (paramSearchDto == null) {
            paramSearchDto = new ParamSearchDto();
        }
        paramSearchDto.setStartUser(startUser);
        return getProcessSituationDtoPage(pageNo, pageSize, paramSearchDto);
    }

    /**
     * 流程分页查询
     */
    public Page<ResponseSituationDto> getProcessSituationDtoPage(@Nullable Integer pageNo, @Nullable Integer pageSize, ParamSearchDto paramSearchDto) {
        pageNo = PageUtil.initPageNo(pageNo);
        pageSize = PageUtil.initPageSize(pageSize);


        if (paramSearchDto == null) {
            throw PlatformException.error("流程查询条件不能为空");
        }

        if (StrUtil.isEmpty(paramSearchDto.getSysid())) {//如不指定查询系统，默认产品系统
            paramSearchDto.setSysid(PlatformConstant.SYS_ID_GATEWAY);
        }

        if (StrUtil.isNotEmpty(paramSearchDto.getBeginDate())) {//yyyymmdd格式
            String beginDate = paramSearchDto.getBeginDate().replaceAll("-", "");
            if (beginDate.length() != 8) {
                throw PlatformException.error("查询日期格式非法");
            }
            paramSearchDto.setBeginDate(beginDate);
        }
        if (StrUtil.isNotEmpty(paramSearchDto.getEndDate())) {//yyyymmdd格式
            String endDate = paramSearchDto.getEndDate().replaceAll("-", "");
            if (endDate.length() != 8) {
                throw PlatformException.error("查询日期格式非法");
            }
            paramSearchDto.setEndDate(endDate);
        }

        if (StrUtil.isNotEmpty(paramSearchDto.getTitle())) {
            paramSearchDto.setTitle(StrUtil.trim(paramSearchDto.getTitle()));
        }
        if (StrUtil.isNotEmpty(paramSearchDto.getProcessDefineKey())) {
            paramSearchDto.setProcessDefineKey(StrUtil.trim(paramSearchDto.getProcessDefineKey()));
        } else {
            paramSearchDto.setProcessDefineKey(
                    "IT_EVENT_MANAGE_FLOW," +
                    "IT_TEST_TASK," +
                    "IT_DEV_MANAGE_FLOW," +
                    "IT_EVENT_IMPROVE_FLOW," +
                    "IT_EQUIPMENT_MODIFY_FLOW," +
                    "IT_EQUIPMENT_APPLY_FLOW," +
                    "IT_EQUIPMENT_OFFLINE_FLOW," +
                    "IT_EQUIPMENT_SERVER_APPLY_FLOW," +
                    "IT_SYSTEM_NAME_APPLY_FLOW," +
                    "IT_SYSTEM_NAME_MODIFY_FLOW," +
                    "ItEventManageFlow," +
                    "ItEventImproveFlow," +
                    "CMDB_PART_STANDARD_MODIFY_FLOW," +
                    "CMDB_PART_IN_FLOW," +
                    "CMDB_PART_OUT_FLOW," +
                    "CMDB_DEVICE_STANDARD_MODIFY_FLOW," +
                    "CMDB_DEVICE_OUT_FLOW," +
                    "CMDB_DEVICE_IN_FLOW," +
                    "IT_VENDOR_RATE" +
                    "");

        }
        if (StrUtil.isNotEmpty(paramSearchDto.getStartUser())) {
            paramSearchDto.setStartUser(StrUtil.trim(paramSearchDto.getStartUser()));
        }


        if (StrUtil.isEmpty(paramSearchDto.getDealUser()) && StrUtil.isEmpty(paramSearchDto.getInvolveUser())
                && StrUtil.isEmpty(paramSearchDto.getStartUser()) && StrUtil.isEmpty(paramSearchDto.getProcessDefineKey())) {
            //TODO 扩展更多流程支持，最好改成配置，zzp
            //flowable接口要求，如果处理人、参与人、发起人均为空，流程类型不能为空
        }
        logger.info("查询flowable流程，查询条件={}", paramSearchDto);
//        Map<String, String> processDefineKeyDict = CacheManager.getDictMapFromRedis(DictConstant.DICT_CODE_PROCESS_DEFINE_KEY);
//        List<String> processDefineKeyList = processDefineKeyDict.keySet().stream().collect(Collectors.toList());
//        String processDefineListStr = String.join(",", processDefineKeyList);
//        if (StrUtil.isEmpty(paramSearchDto.getProcessDefineKey())) {//由于processDefineKey必须有，如无则检索系统支持的所有流程
//            paramSearchDto.setProcessDefineKey(processDefineListStr);
//        }
        FlowableResponse<PageDataResult<ResponseSituationDto>> result = Strings.isNotBlank(paramSearchDto.getInvolveUser())
                ? feignFlowableService.searchInvolveUserProcessList(pageNo, pageSize, paramSearchDto)
                : feignFlowableService.searchProcessList(pageNo, pageSize, paramSearchDto);

        Pageable pageable = PageRequest.of(pageNo - 1, pageSize);
        return Objects.nonNull(result.getData()) ? new PageImpl<>(result.getData().getList(), pageable, result.getData().getTotals()) : new PageImpl<>(Collections.EMPTY_LIST, pageable, 0);
    }


    /**
     * 流程撤销
     */
    @Transactional
    public FlowableResponse processStop(ParamStopDto paramStopDto) {
        logger.info("FlowableFeignService流程撤销:入参paramStopDto：{}", JsonTool.objectToJson(paramStopDto));
        if (paramStopDto == null || StrUtil.isEmpty(paramStopDto.getProcessId())) {
            throw PlatformException.error("撤销流程参数为空,paramStopDto:" + paramStopDto);
        }

        //TODO 回滚，业务逻辑的特殊处理

//        //设置流程为强制取消状态，已经暗含在：flowableRollbackStrategy.handleRollback
//        flowableProcessInfoService.forceEndProcessById(paramStopDto.getProcessId());

        FlowableResponse ret = feignFlowableService.stopProcessInstanceById(paramStopDto.getProcessId(), requestTool.getAccountFromRequest());

        this.validFlowableResponse(ret);

        //如果是事件流程，则标记一下状态
        FlowFormEvent event = flowFormEventRepo.findByProcessId(paramStopDto.getProcessId());
        if (event != null) {
            event.setIsClose(DictConstant.EVENT_FORCE_CLOSE);
            flowFormEventRepo.save(event);
        }

        logger.info("FlowableFeignService驳回流程，返回ret：" + ret);
        return ret;
    }


    /**
     * 查询流程评注意见
     *
     * @param paramCommentDto
     * @return
     */
    public Page<ResponseCommentDto> queryCommentsByProcessId(Integer pageNo, Integer pageSize, ParamCommentDto paramCommentDto) {
        FlowableResponse<PageDataResult<ResponseCommentDto>> response = feignFlowableService.queryCommentsByProcessId(pageNo, pageSize, paramCommentDto);

        if (response.getStatus() == 0) {
            Pageable pageable = PageRequest.of(pageNo - 1, pageSize);
            Page<ResponseCommentDto> resultPage = new PageImpl(response.getData().getList(), pageable, response.getData().getTotals());

            return resultPage;
        } else {
            return Page.empty();
        }
    }

    /**
     * 获取流程图
     *
     * @param
     * @return
     */
    public void genProcessDiagram(HttpServletRequest request, HttpServletResponse httpResponse, String processId) throws Exception {
        ResponseEntity<byte[]> responseDiagramData = feignFlowableService.processDiagramStream(processId);

        String userAgent = request.getHeader("User-Agent");
        String fileName = "流程图-" + processId + ".png";
        // 针对IE或者以IE为内核的浏览器：
        if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {
            fileName = java.net.URLEncoder.encode(fileName, "UTF-8");
        } else {
            // 非IE浏览器的处理：
            fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
        }
//        httpResponse.setContentType("application/octet-stream");
        httpResponse.setContentType("image/png");
//        httpResponse.setHeader("Content-Disposition", "attachment;filename=" + fileName);//注意掉文件名后，可以在线查看；如果这里设置文件名，会另存为图片

        OutputStream output = httpResponse.getOutputStream();
        output.write(responseDiagramData.getBody());
        output.flush();
        output.close();

        logger.info(httpResponse.toString());
    }


    /**
     * 流程我的抄送分页查询
     *
     * @param processDefineKey 流程类型，多个逗号分割字符串
     */
    public Page<FlowableCopyDto> findProcessCopyPage(String account, String status, String processDefineKey, Integer pageNo, Integer pageSize) {
        pageNo = PageUtil.initPageNo(pageNo);
        pageSize = PageUtil.initPageSize(pageSize);

        ParamGetCopyListDto params = new ParamGetCopyListDto();
        params.setSysid(PlatformConstant.SYS_ID_GATEWAY);
        if (StrUtil.isNotEmpty(account)) {
            params.setUsernameDestination(account);
        } else {
            throw PlatformException.error("查询抄送时，需要提供抄送接收人信息");
        }

        //网关，flowable，流程待阅状态：状态0未查看1已查看，这里做字典适配
        if (StrUtil.isNotEmpty(status)) {
            if ("Y".equals(status)) {
                status = "1";
            } else if ("N".equals(status)) {
                status = "0";
            }
            params.setStatus(status);
        }

        if (StrUtil.isNotEmpty(processDefineKey)) {
            processDefineKey = StrUtil.trim(processDefineKey.trim());
            params.setProcessDefinitionKey(processDefineKey);
        }
//        params.setProcessDefinitionKey();
        FlowableResponse<PageDataResult<FlowableCopyDto>> response = feignFlowableService.getCopyProcessList(pageNo, pageSize, params);

        if (response.getStatus() == 0) {
            Pageable pageable = PageRequest.of(pageNo - 1, pageSize);
            Page<FlowableCopyDto> resultPage = new PageImpl(response.getData().getList(), pageable, response.getData().getTotals());
            return resultPage;
        } else {
            return Page.empty();
        }
//        TODO, 网关抄送分页，目前这里返回全部，20220316
//        FlowableResponse<ResponseCopyListDto> copyListDtoFlowableResponse = feignFlowableService.getCopyProcessList(account, PlatformConstant.SYS_ID_GATEWAY, status);
//        logger.info("流程抄送返回：{}", copyListDtoFlowableResponse.toString());
//
//        pageNo = PageUtil.initPageNo(pageNo);
//        pageSize = PageUtil.initPageSize(pageSize);
//        Pageable pageable = PageRequest.of(pageNo - 1, pageSize);
//        List<FlowableCopy> totalCopyList = null;
//        List<FlowableCopy> pageContentCopyList = null;
//        try {
//            totalCopyList = copyListDtoFlowableResponse.getData().getCopyList();
//
//            pageContentCopyList = totalCopyList.subList((int) pageable.getOffset(), (int) Math.min(totalCopyList.size(), pageable.getOffset() + pageable.getPageSize()));
//        } catch (Exception e) {
//            logger.info("流程抄送返回解析异常：", e);
//        }
//        if (totalCopyList == null) {
//            totalCopyList = new ArrayList<>();
//        }
//        if (pageContentCopyList == null) {
//            pageContentCopyList = new ArrayList<>();
//        }
//        logger.info("流程抄送返回：{}", totalCopyList);
//        Page<FlowableCopy> resultPage = new PageImpl(pageContentCopyList, pageable, totalCopyList.size());
//
//        return resultPage;
    }

    /**
     * 为用户增加流程抄送，不增加评论
     *
     * @param accountList 抄送人列表
     * @param processId   流程ID
     * @param sendAccount 发起抄送人员
     */
    public FlowableResponse<ResponseMsgDto> copyProcess(@NotNull String processId, List<String> accountList, String sendAccount, String remark) {
        return copyProcess(processId, null,  accountList, sendAccount, remark);
    }

    /**
     * 为用户增加流程抄送
     *
     * @param accountList 抄送人列表
     * @param processId   流程ID
     * @param sendAccount 发起抄送人员
     */
    public FlowableResponse<ResponseMsgDto> copyProcess(@NotNull String processId, @Nullable String taskId, List<String> accountList, String sendAccount, String remark) {
        if (CollectionUtil.isEmpty(accountList) || StrUtil.isEmpty(processId)) {
            return null;
        }

        ParamCopyDto copyParam = new ParamCopyDto();
        copyParam.setProcessId(processId);
        copyParam.setSysid(PlatformConstant.SYS_ID_GATEWAY);
        copyParam.setAddComment(true);
        copyParam.setTaskId(taskId);
        copyParam.setUsernameSource(sendAccount);
        copyParam.setUsernameDestination(String.join(",", accountList));
        copyParam.setRemark(remark);

        logger.info("为用户增加流程抄送增加:入参paramStopDto：" + JsonTool.objectToJson(copyParam));
        FlowableResponse<ResponseMsgDto> ret = feignFlowableService.copyProcess(copyParam);
        logger.info("返回ret：" + ret);
        if (validFlowableResponse(ret)) {
            return ret;
        }
        return ret;
    }


    /**
     * 更新抄送状态任务为已读状态
     *
     * @return
     */
    public ResponseCopyDetailDto copyProcessRead(String processId, String operator) {
        ParamCopyDetailDto paramCopyDetailDto = new ParamCopyDetailDto();
        paramCopyDetailDto.setProcessId(processId);
        paramCopyDetailDto.setUserId(operator);
        paramCopyDetailDto.setSysid(PlatformConstant.SYS_ID_GATEWAY);

        logger.info("入参processId：" + processId);
        FlowableResponse<ResponseCopyDetailDto> ret = feignFlowableService.getCopyProcessDetail(paramCopyDetailDto);
        logger.info("copyProcessRead:返回ret：" + ret);
        ResponseCopyDetailDto returnData = null;
        if (ret != null && ret.getStatus() == 0) {
            returnData = ret.getData();
        }
        return returnData;
    }

    /**
     * 转办任务
     *
     * @param turnTaskDto 转办信息
     * @return
     */
    public ResponseTurnDto turnTask(TurnTaskDto turnTaskDto) {

        String taskId = turnTaskDto.getTaskId();
        String taskFromAccount = turnTaskDto.getTaskFromAccount();
        String taskToAccount = turnTaskDto.getTaskToAccount();
        String comment = turnTaskDto.getTurnTaskComment();
        if (StrUtil.isEmpty(taskId) || StrUtil.isEmpty(taskToAccount)) {
            throw PlatformException.error("转办任务时，任务ID或转办处理人不能为空");
        }
        FlowableResponse<ResponseSituationDto> taskInfo = this.getTaskInfo(taskId, PlatformConstant.SYS_ID_GATEWAY);
        if (taskInfo == null || taskInfo.getStatus() != 0) {
            throw PlatformException.error(String.format("查询任务%s失败：%s", taskId, taskInfo.getMsg()));
        }

        ParamTurnDto paramTurnDto = new ParamTurnDto();
        paramTurnDto.setSysid(PlatformConstant.SYS_ID_GATEWAY);
        paramTurnDto.setUsernameDestination(taskToAccount);
        paramTurnDto.setUsernameSource(taskFromAccount);
        paramTurnDto.setTaskId(taskId);
        paramTurnDto.setComment(comment);

        logger.info("入参 ParamTurnDto：" + paramTurnDto);
        FlowableResponse<ResponseTurnDto> ret = feignFlowableService.turnTask(paramTurnDto);
        logger.info("turnTask:返回ret：" + ret);
        ResponseTurnDto returnData = null;
        if (ret != null && ret.getStatus() == 0) {
            returnData = ret.getData();
        }
        return returnData;
    }


    /**
     * 增加任务评论
     *
     * @param paramAddTaskCommentDto 任务评论
     * @return
     */
    public ResponseMsgDto addTaskComment(ParamAddTaskCommentDto paramAddTaskCommentDto) {
        if (StrUtil.isEmpty(paramAddTaskCommentDto.getProcessId()) || StrUtil.isEmpty(paramAddTaskCommentDto.getTaskId())
                || StrUtil.isEmpty(paramAddTaskCommentDto.getUserId())) {
            throw PlatformException.error("增加任务评论时，流程ID、任务ID或处理人不能为空");
        }
        logger.info("入参 ParamAddTaskCommentDto：" + paramAddTaskCommentDto);
        FlowableResponse<ResponseMsgDto> ret = feignFlowableService.addTaskComment(paramAddTaskCommentDto);
        logger.info("addTaskComment:返回ret：" + ret);
        validFlowableResponse(ret);
        return ret.getData();
    }


    /**
     * 删除流程
     *
     * @param paramDeleteDto paramDeleteDto.processIdList  删除流程ID列表
     * @return
     */
    public ResponseMsgDto deleteProcess(ParamDeleteDto paramDeleteDto) {
        if (paramDeleteDto == null || CollectionUtil.isEmpty(paramDeleteDto.getProcessIdList())) {
            throw PlatformException.error("删除流程时，流程ID列表不能为空");
        }
        logger.info("入参 processIdList：{}", paramDeleteDto.getProcessIdList().toString());
        FlowableResponse<ResponseMsgDto> ret = feignFlowableService.deleteProcess(paramDeleteDto);
        logger.info("FlowableService.deleteProcess:返回ret：{}", ret);
        validFlowableResponse(ret);
        return ret.getData();
    }

    /**
     * 调用网关批量上传附件接口
     */
    public ResponseUploadDto uploadDoc(MultipartFile[] file) {
        if (file == null || file.length == 0) {
            throw PlatformException.error("调用网关批量上传附件接口，附件不能为空");
        }
        for (MultipartFile f : file) {
            logger.info(f.getOriginalFilename());
        }
        FlowableResponse<ResponseUploadDto> ret = null;
        try {
            ret = feignFlowableService.uploadDoc(file);
        } catch (IOException e) {
            logger.info("FlowableService.uploadDoc:异常：{}",
                    e);
        }

        logger.info("FlowableService.uploadDoc:返回ret：{}", ret);
        return ret.getData();
    }

    /**
     * 查询流程变量
     *
     * @return
     */
    public TreeMap<String, Object> getVariableByProcessId(String processId) {
        if (StrUtil.isEmpty(processId)) {
            throw PlatformException.error("流程ID不能为空");
        }

        logger.info("入参 processId：{}", processId);
        FlowableResponse<ParamSetupVariableDto> ret = feignFlowableService.getVariableByProcessId(processId);
        logger.info("getVariableByProcessId:返回ret：{}", ret);
        validFlowableResponse(ret);
        ParamSetupVariableDto variableDto = ret.getData();

        if (variableDto == null || variableDto.getSetupMap() == null || variableDto.getSetupMap().get(processId) == null) {
            return new TreeMap<>();
        } else {
            Map<String, Object> map = variableDto.getSetupMap().get(processId);

            TreeMap<String, Object> treeMap = new TreeMap<>();//根据key排序
            Set<String> keySet = map.keySet();
            Iterator<String> iter = keySet.iterator();
            while (iter.hasNext()) {
                String key = iter.next();
                treeMap.put(key, map.get(key));
            }
            return treeMap;
        }
    }

    /**
     * 设置流程变量
     *
     * @return
     */
    public String setupVariableByProcessId(ParamSetupVariableSimpleDto variableDto) {
        if (variableDto == null || StrUtil.isEmpty(variableDto.getProcessId()) || MapUtil.isEmpty(variableDto.getVariableMap())) {
            throw PlatformException.error("设置变量不能为空");
        }

        ParamSetupVariableDto paramSetupVariableDto = new ParamSetupVariableDto();
        Map<String, Map<String, Object>> setupMap = new HashMap<>();
        setupMap.put(StrUtil.trim(variableDto.getProcessId()), variableDto.getVariableMap());
        paramSetupVariableDto.setSetupMap(setupMap);

        logger.info("入参 ：{}", paramSetupVariableDto.toString());
        FlowableResponse<String> ret = feignFlowableService.setupVariableByProcessId(paramSetupVariableDto);
        logger.info("返回ret：{}", ret);
        validFlowableResponse(ret);
        return ret.getData();
    }


    /**
     * 更新流程标题
     *
     * @param paramUpdateTitleDto
     * @return 返回更新结果
     */
    public FlowableResponse<ResponseMsgDto> updateProcessTitle(ParamUpdateTitleDto paramUpdateTitleDto) {
        if (Objects.isNull(paramUpdateTitleDto) || StrUtil.isEmpty(paramUpdateTitleDto.getProcessId()) || StrUtil.isEmpty(paramUpdateTitleDto.getTitle())) {
            throw PlatformException.error("更新流程标题，必要参数为空," +
                    (Objects.isNull(paramUpdateTitleDto) ? "请求参数对象为空" : paramUpdateTitleDto.toString()));
        }
        logger.info("流程-{}，修改标题为-{}", paramUpdateTitleDto.getProcessId(), paramUpdateTitleDto.getTitle());


        return feignFlowableService.updateProcessTitle(paramUpdateTitleDto);
    }


    /**
     * 查询某个用户与流程的抄送关系记录
     */
    public ResponseCopyUserDto getCopyProcessOfUser(ParamCopyUserDto paramCopyUserDto) {
        if (paramCopyUserDto == null || StrUtil.isEmpty(paramCopyUserDto.getUserId())
                || StrUtil.isEmpty(paramCopyUserDto.getProcessId())
        ) {
            throw PlatformException.error("查询某个用户与流程的抄送关系，流程ID、用户ID等必要信息不能为空");
        }
        if (StrUtil.isEmpty(paramCopyUserDto.getSysid())) {
            paramCopyUserDto.setSysid(PlatformConstant.SYS_ID_GATEWAY);
        }

        logger.info("getCopyProcessOfUser 查询用户与流程的抄送关系记录:入参：{}", paramCopyUserDto);
        FlowableResponse<ResponseCopyUserDto> response = feignFlowableService.getCopyProcessOfUser(paramCopyUserDto);
        logger.info("getCopyProcessOfUser 查询用户与流程的抄送关系记录:返回ret：{}", response);
        validFlowableResponse(response);
        return response.getData();
    }

    /**
     * 查询某个用户信息
     *
     * @param userId 用户OA账号
     */
    public LdapEhrEmpDto getUserInfoByAccount(String userId) {
        if (StrUtil.isEmpty(userId)) {
            return null;
        }

        logger.info("getUserInfoByAccount 查询用户:入参：{}", userId);
        AppEhrResponse<LdapEhrEmpDto> response = iFeignLdapService.getLdapEhrEmp(userId, null, false);
        logger.info("getUserInfoByAccount 查询用户:返回ret：{}", response);
        if (null == response) {
            logger.info("Ldap接口返回为空");
            throw PlatformException.error("Ldap接口返回为空");
        }

        logger.info("Ldap response:{}", response.toString());
        if (!response.isSuccess()) {//返回0是正常
            String errMsg = "Ldap接口返回异常，错误状态码=" + response.getStatus() + "，原因：" + response.getErrorMsg();
            logger.error(errMsg);
            throw PlatformException.error(errMsg);
        } else {
            logger.info("Ldap接口返回正常，返回信息=" + response.toString());
        }
        return response.getData();
    }


    /**
     * 转发任务
     */
    public ForwardFlowableResponse forwardFlowableByTask(ForwardFlowableParamDto forwardParam) {
        if (forwardParam == null || CollectionUtil.isEmpty(forwardParam.getUserList())
                || StrUtil.isEmpty(forwardParam.getTaskId())) {
            throw PlatformException.error("转发任务必要信息为空");
        }
        if (StrUtil.isEmpty(forwardParam.getComment())) {
            forwardParam.setTaskId("转发任务");
        }

        logger.info("FlowableFeignService查看流程实例是否结束:入参processId：{}", forwardParam);
        FlowableResponse<ForwardFlowableResponse> response = feignFlowableService.forwardFlowableByTask(forwardParam);
        logger.info("返回ret：{}", response);
        validFlowableResponse(response);
        return response.getData();
    }

    public FlowableResponse<ResponseMsgDto> backToSelect(ParamRollbackDto param) {
        return feignFlowableService.return2SelectNode(param);
    }

    public List<TaskDefinitionDto> getAllSourceList(@NotNull String taskId) {
        return feignFlowableService.getAllSourceList(taskId).getData();
    }

    /**
     * 通过用户列表和流程类型列表查询所有待办
     */
    public List<ResponseSituationDto> findAllProcessTodoByAccountAndType(List<String> accList, List<String> typeList) {
        ParamSearchDto param = new ParamSearchDto();
        param.setDealUserList(accList);
        param.setProcessDefineKeyList(typeList);
        param.setSysid(PlatformConstant.SYS_ID_GATEWAY);
        //TODO:微服务调用flowable分页查询，先按大页查询
        FlowableResponse<PageDataResult<ResponseSituationDto>> rep = feignFlowableService.searchDealProcessList(1, 9999, param);
        return Objects.nonNull(rep.getData()) ? rep.getData().getList() : Collections.EMPTY_LIST;
    }


    /**
     * 查看流程实例是否结束
     */
    public Boolean checkProcessEnd(String processId) {
        if (StrUtil.isEmpty(processId)) {
            return null;
        }

        boolean ret = feignFlowableService.checkProcessEnd(processId);
        logger.info("FlowableFeignService查看流程实例是否结束:返回ret：" + ret);
        return ret;
    }
}
